#pragma once

#include "Observer.h"
#include "SimulationTime.h"
#include <string>
#include <vector>
#include <algorithm>
#include "Link.h"
/*
  Copyright 2008 by Alexey Solovyev and University of Pittsburgh
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

namespace org
{
	namespace spark
	{
		namespace core
		{
			//class Link;
			//using org::spark::core::Link;
			//using org::spark::math::SimulationTime;


			// TODO: die immediately and die via adding to die queue

			/// <summary>
			/// Represents an abstract agent
			/// </summary>
			class Agent 
			{
				/// <summary>
				/// Default serialVersionUID
				/// </summary>
			private:
				//static const long long serialVersionUID = 1LL;

				// Internal private variable which assigns the unique id to each agent
				// TODO: Is it useful?
				int id;
				static int idCounter;

				// True when agent is dead. Can be accessed using isDead() method.
			public:
				bool dead;

				// TODO: is there a better way? (without introducing new variable inside agent class)
				// "deep" serialization means that the agent during serialization automatically
				// removed from the observer and space and then completely restored during
				// deserealization
			protected:
				bool deepSerialization;

				// Used by Observer2 to create a linked list of agents
				// TODO: restore after serialization somehow, need to check the observer type (maybe)
			public:
				Agent *prev, *next;


				// TODO: there are problems with links and spatial parallelization,
				// so links list is transient
				std::vector<Link*> links;

				/// <summary>
				/// Adds a link to the list of links </summary>
				/// <param name="link"> </param>
				virtual void addLink(Link *link);

				/// <summary>
				/// Removes a link from the list of links </summary>
				/// <param name="link"> </param>
				virtual void removeLink(Link *link);


				/// <summary>
				/// Returns all links connected to the agent 
				/// @return
				/// </summary>
				virtual std::vector<Link*> getLinks();


				/// <summary>
				/// Returns a link connecting two agents </summary>
				/// <param name="a"> </param>
				/// <returns> null if no such a link exists </returns>
				virtual Link *getFirstConnection(Agent *a);


				/// <summary>
				/// Returns a link of the given type connecting two agents </summary>
				/// <param name="a"> </param>
				/// <returns> null if no such a link exists </returns>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
// Most Java annotations will not have direct native C++ equivalents:
//ORIGINAL LINE: @SuppressWarnings("unchecked") public <T extends Link> T getFirstConnection(Agent a, Class linkType)
				T getFirstConnection(Agent *a, Class *linkType);


				/// <summary>
				/// Returns all links of the given type </summary>
				/// @param <T> </param>
				/// <param name="type">
				/// @return </param>
// There is no native C++ template equivalent to generic constraints:
				template<typename T> 
// Most Java annotations will not have direct native C++ equivalents:
//ORIGINAL LINE: @SuppressWarnings("unchecked") public <T extends Link> java.util.ArrayList<T> getLinks(Class type)
				 std::vector<T> getLinks(Class *type);



				/// <summary>
				/// Changes the type of the serialization
				/// Use with caution! </summary>
				/// <param name="deepSerialization"> </param>
				virtual void setDeepSerialization(bool deepSerialization);

				/// <summary>
				/// Checks whether the agent is dead </summary>
				/// <returns> true if the agent is dead and false otherwise </returns>
				virtual bool isDead();

				/// <summary>
				/// Default protected constructor.
				/// Automatically adds the agent to the context.
				/// </summary>
			protected:
				Agent();

				// A trick to use this constructor internally only
				// for initialization without adding to Observer's list of agents
			public:
				Agent(int trick);

				/// <summary>
				/// Removes the agent from context
				/// </summary>
				virtual void die();

				/// <summary>
				/// @deprecated
				/// This is the main function of each agent. Any derived class
				/// should override this function. All work of agent is done inside
				/// this function </summary>
				/// <param name="tick"> a number of ticks passed since the model setup </param>
				virtual void step(long long tick);

				/// <summary>
				/// This is the main function of each agent. Any derived class
				/// should override this function. All work of agent is done inside
				/// this function </summary>
				/// <param name="time"> represents the simulation time </param>
				virtual void step(SimulationTime *time);

				std::string getClass();
				/// <summary>
				/// Returns special data for this agent </summary>
				/// <returns> null if no special data is available  </returns>
				//virtual AgentData *getData();


				/// <summary>
				/// Gives some information about agent. Specifically, the class of the agent
				/// and its id
				/// </summary>
				virtual std::string ToString();

				// TODO: better implementation required
				/// <summary>
				/// Returns id of the agent.
				/// </summary>
				virtual int GetHashCode();
				
				/// <summary>
				/// Custom deserialization is needed.
				/// </summary>
			private:
				//void readObject(ObjectInputStream *ois); //throw(IOException, ClassNotFoundException);

				/// <summary>
				/// Custom serialization is needed.
				/// </summary>
				//void writeObject(ObjectOutputStream *oos); //throw(IOException);


			private:
				void InitializeInstanceFields();
			};

			

		}
	}
}
